package com.yungu.swift.autoconfig.lock;

import com.yungu.swift.autoconfig.redis.RedisStringCacheService;

import java.util.concurrent.TimeUnit;

/**
 * @author : cuixiuyin
 * @date : 2019/9/9
 */
public class RedisLock {

    private static final Long DEFAULT_TIMEOUT = 3000L;

    private RedisStringCacheService redisCacheService;

    public RedisLock(RedisStringCacheService redisCacheService) {
        this.redisCacheService = redisCacheService;
    }

    /**
     * 多线程争抢锁
     *
     * @param key 锁的key
     * @return true 表示获取锁成功  false 表示获取锁失败
     * @throws InterruptedException
     */
    public boolean lock(String key) throws InterruptedException {
        return lock(key, DEFAULT_TIMEOUT);
    }


    /**
     * 多线程争抢锁
     *
     * @param key     锁的key
     * @param timeout 锁定时间，默认3秒，3秒后如果线程未主动释放锁，可被其他线程强行争抢，用来防止锁未正常释放
     * @return true 表示获取锁成功  false 表示获取锁失败
     * @throws InterruptedException
     */
    public boolean lock(String key, Long timeout) throws InterruptedException {
        long millis = System.currentTimeMillis();
        long expire = millis + timeout + 1;
        while (true) {
            boolean setnx = redisCacheService.setnx(key, String.valueOf(expire));
            if (setnx == true) {
                break;
            }
            String oldTimeout = redisCacheService.get(key);
            String oldValue = redisCacheService.getSet(key, String.valueOf(expire));
            if (millis > Long.valueOf(oldTimeout) && millis > Long.valueOf(oldValue)) {
                break;
            }
            // 0.1 秒后继续尝试获取锁
            Thread.sleep(100);
        }
        return true;
    }


    /**
     * 唯一锁 —— 表示只有唯一一个线程可以获得锁。默认 3 秒释放，用于锁未正常释放的情况
     *
     * @param key
     * @return
     */
    public boolean uniqueLock(String key) {
        return uniqueLock(key, DEFAULT_TIMEOUT.intValue());
    }

    public boolean uniqueLock(String key, int timeout) {
        Long incr = redisCacheService.incr(key);
        if (incr > 1) {
            return false;
        }
        redisCacheService.expire(key, timeout, TimeUnit.MILLISECONDS);
        return true;
    }


    /**
     * 释放锁
     *
     * @param key 锁的key
     */
    public void unLock(String key) {
        redisCacheService.del(key);
    }

}
